const schedule = require('node-schedule');
const { getLogger } = require('../logger');
const logger = getLogger('infra.scheduler');
const config = require('../../config');

class TaskScheduler {
    constructor() {
        this.tasks = new Map();
        this.failedTasks = new Map();
    }

    shouldPauseTask(taskId) {
        const failInfo = this.failedTasks.get(taskId);
        if (!failInfo) return false;
        const { failureCount, pauseUntil } = failInfo;
        if (pauseUntil > Date.now()) return true;
        if (failureCount >= 3) {
            const coolDownTime = Math.min(300000, Math.pow(2, failureCount - 1) * 10000);
            this.failedTasks.set(taskId, {
                ...failInfo,
                pauseUntil: Date.now() + coolDownTime,
                failureCount: failureCount + 1
            });
            logger.warn(`任务 ${taskId} 已连续失败${failureCount}次，将暂停${coolDownTime / 1000}秒`);
            return true;
        }
        return false;
    }

    resetTaskFailureCount(taskId) {
        this.failedTasks.delete(taskId);
    }

    recordTaskFailure(taskId, error) {
        const failInfo = this.failedTasks.get(taskId) || {
            failureCount: 0,
            lastFailureTime: 0,
            pauseUntil: 0
        };
        this.failedTasks.set(taskId, {
            failureCount: failInfo.failureCount + 1,
            lastFailureTime: Date.now(),
            pauseUntil: failInfo.pauseUntil,
            lastError: error.message
        });
    }

    addTask(taskId, cronExpression, taskFunction, options = {}) {
        if (this.tasks.has(taskId)) {
            logger.warn(`任务 ${taskId} 已存在，将被替换`);
            this.removeTask(taskId);
        }
        const wrappedTaskFunction = async () => {
            if (this.shouldPauseTask(taskId)) {
                const failInfo = this.failedTasks.get(taskId);
                const remainingPause = Math.round((failInfo.pauseUntil - Date.now()) / 1000);
                logger.debug(`任务 ${taskId} 仍在暂停中，剩余${remainingPause}秒`);
                return;
            }
            try {
                await taskFunction();
                this.resetTaskFailureCount(taskId);
            } catch (error) {
                this.recordTaskFailure(taskId, error);
                const failInfo = this.failedTasks.get(taskId);
                if (failInfo.failureCount <= 3) {
                    logger.error(`任务 ${taskId} 执行失败 (第${failInfo.failureCount}次):`, { error });
                } else {
                    logger.warn(`任务 ${taskId} 执行失败 (第${failInfo.failureCount}次)，已自动暂停:`, { error });
                }
            }
        };

        let job;
        if (typeof cronExpression === 'number') {
            logger.info(`⏱️ 添加间隔任务: ${taskId}, 间隔: ${cronExpression / 1000}秒`);
            job = setInterval(wrappedTaskFunction, cronExpression);
        } else {
            logger.info(`⏰ 添加定时任务: ${taskId}, 表达式: ${cronExpression}`);
            job = schedule.scheduleJob(cronExpression, wrappedTaskFunction);
        }
        this.tasks.set(taskId, { job, options, wrappedTaskFunction });
        return job;
    }

    addFromConfig(taskKey, taskFunction, options = {}) {
        const s = config.schedules || {};
        const item = s[taskKey];
        if (!item || !item.cron) {
            logger.warn(`未找到定时任务配置: ${taskKey}`);
            return null;
        }
        return this.addTask(taskKey, item.cron, taskFunction, { ...options, description: item.description });
    }

    removeTask(taskId) {
        const task = this.tasks.get(taskId);
        if (task) {
            if (typeof task.job === 'object' && task.job.cancel) {
                task.job.cancel();
            } else {
                clearInterval(task.job);
            }
            this.tasks.delete(taskId);
            this.failedTasks.delete(taskId);
            logger.info(`❌ 移除任务: ${taskId}`);
        }
    }

    removeAllTasks() {
        this.tasks.forEach((_, taskId) => this.removeTask(taskId));
        this.failedTasks.clear();
        logger.info('🗑️  移除所有定时任务');
    }

    getAllTasks() {
        return Array.from(this.tasks.keys()).map(taskId => {
            const failInfo = this.failedTasks.get(taskId);
            return {
                taskId,
                status: failInfo ? {
                    failed: true,
                    failureCount: failInfo.failureCount,
                    lastError: failInfo.lastError,
                    nextAttempt: failInfo.pauseUntil > Date.now() ? new Date(failInfo.pauseUntil).toISOString() : 'Now'
                } : { failed: false }
            };
        });
    }

    shutdown() {
        this.removeAllTasks();
        logger.info('🛑 定时任务管理器已关闭');
    }
}

module.exports = new TaskScheduler();